﻿//************************************************************************************************
// Copyright © 2010 Steven M. Cohn. All Rights Reserved.
//
// Stolen from iTuner!  (Which I wrote, so I guess it's not really stealing)
//
//************************************************************************************************

namespace CookieChipper
{
	using System;
	using System.Linq;
	using System.Net;
	using System.Windows.Forms;
	using System.Xml.Linq;
	using System.ComponentModel;


	/// <summary>
	/// 
	/// </summary>
	/// <param name="release"></param>

	internal delegate void UpgradeHandler (UpgradeRelease release);


	/// <summary>
	/// Retrieves the codeplex Releases RSS feed for this project and, if a new release
	/// is available, displays an upgrade dialog to the user.
	/// </summary>

	internal class Upgrader : IDisposable
	{
		private const string ReleaseRss =
			"http://cookiechipper.codeplex.com/project/feeds/rss?ProjectRSSFeed=codeplex%3a%2f%2frelease%2fcookiechipper";

		private Control owner;
		private BackgroundWorker worker;
		private bool isDisposed;


		/// <summary>
		/// Initializer a new upgrader for the specified owner.
		/// </summary>
		/// <param name="owner"></param>

		public Upgrader (Control owner)
		{
			this.owner = owner;
			this.worker = null;
			this.isDisposed = false;
		}


		public void Dispose ()
		{
			Dispose(true);
		}


		protected virtual void Dispose (bool disposing)
		{
			if (!isDisposed)
			{
				if (worker != null)
				{
					worker.Dispose();
				}

				isDisposed = true;
			}
		}


		public event UpgradeHandler UpgraderCompleted;


		/// <summary>
		/// Begins an asynchronous background request to the retrieve recent release information.
		/// </summary>

		public void CheckUpgrades ()
		{
			if (NetworkStatus.IsAvailable)
			{
				worker = new BackgroundWorker();
				worker.DoWork += new DoWorkEventHandler(DoWork);
				worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoWorkCompleted);
				worker.WorkerSupportsCancellation = true;
				worker.RunWorkerAsync(owner);
			}
		}


		/// <summary>
		/// Retrieve the Releases RSS feed and parse the items to determine the most recent
		/// release.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>

		private void DoWork (object sender, DoWorkEventArgs e)
		{
			string xml = null;

			try
			{
				// call home to the codeplex project Releases RSS feed
				using (WebClient client = new WebClient())
				{
					xml = client.DownloadString(ReleaseRss);
				}
			}
			catch
			{	
			}

			if (!String.IsNullOrEmpty(xml))
			{
				UpgradeRelease release = null;

				try
				{
					XElement root = XElement.Parse(xml, LoadOptions.None);
					XNamespace ns = root.GetDefaultNamespace();

					// This is specific to the title string used in the CookieChipper project
					// where titles are of the form:
					//
					//     Updated Release: CookeChipper 1.2.3738 Beta 2 (Mar 27, 2010)
					//
					// The version string is always of the format "major.minor.build" but the
					// build part might be exactly "xxxx" for "planned" releases.  Since releases
					// are given in reverse-chronological order, we can extract the first item
					// ignoring any items that contains ".xxxx"

					release =
						(from item in
							 ((from x in root
								   .Elements("channel")
								   .Elements("item")
							   where !(x.Element("title").Value.Contains(".xxxx") ||
									   x.Element("title").Value.Contains("Beta"))
							   select x).Take(1))
						 select new UpgradeRelease(
							 item.Element("title").Value,
							 item.Element("link").Value,
							 item.Element("pubDate").Value,
							 item.Element("description").Value
						 )).Single();
				}
				catch (Exception)
				{
					release = null;
				}

				release.Owner = e.Argument as Control;

				e.Result = release;
			}
		}


		/// <summary>
		/// If we have a valid release and it is later than the current running release
		/// then display the Upgrade dialog.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>

		private void DoWorkCompleted (object sender, RunWorkerCompletedEventArgs e)
		{
			UpgradeRelease release = e.Result as UpgradeRelease;

			if ((release == null) || !release.IsUpgradeAvailable)
			{
				return;
			}

			if (release.Owner.InvokeRequired)
			{
				release.Owner.BeginInvoke(new Action<UpgradeRelease>(Complete), release);
			}
			else
			{
				Complete(release);
			}
		}


		private void Complete (UpgradeRelease release)
		{
			if (UpgraderCompleted != null)
			{
				UpgraderCompleted(release);
			}
		}
	}
}
